package com.tanhua.dubbo.server.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.pojo.UserLike;
import com.tanhua.dubbo.server.vo.PageInfo;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Impl 探花交友
 *
 * @author zZZFf
 * @since 2021/7/10 11:03
 */
@Service(version = "1.0.0")
public class UserLikeApiImpl implements UserLikeApi {
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    public static final String LIKE_REDIS_KEY_PREFIX = "USER_LIKE_";
    public static final String NOT_LIKE_REDIS_KEY_PREFIX = "USER_NOT_LIKE_";

    /**
     * 喜欢
     *
     * @param userId     用户id
     * @param likeUserId 喜欢用户id
     * @return
     */
    @Override
    public Boolean likeUser(Long userId, Long likeUserId) {
        // 先判断用户是否喜欢
        if (this.isLike(userId, likeUserId)) {
            return false;
        }

        UserLike userLike = new UserLike();
        userLike.setId(ObjectId.get()); // 主键id
        userLike.setUserId(userId); // 用户id
        userLike.setLikeUserId(likeUserId); // 喜欢用户id
        userLike.setCreated(System.currentTimeMillis()); // 创建时间

        // 将数据保存到MongoDB中
        this.mongoTemplate.save(userLike);

        // 用户的喜欢数据保存到Redis
        String redisKey = this.getLikeRedisKey(userId);
        String hashKey = String.valueOf(likeUserId);
        this.redisTemplate.opsForHash().put(redisKey, hashKey, "1");

        // 判断，喜欢的用户是否在不喜欢的列表中，如果在，就需要删除数据
        if (this.isNotLike(userId, likeUserId)) {
            redisKey = this.getNotLikeRedisKey(userId);
            this.redisTemplate.opsForHash().delete(redisKey, hashKey);
        }

        return true;
    }

    /**
     * 不喜欢
     *
     * @param userId        用户id
     * @param notLikeUserId 喜欢用户id
     * @return
     */
    @Override
    public Boolean notLikeUser(Long userId, Long notLikeUserId) {
        // 判断用户是否不喜欢
        if (this.isNotLike(userId, notLikeUserId)) {
            return false;
        }

        // 在Redis中添加用户不喜欢
        String redisKey = this.getNotLikeRedisKey(userId);
        String hashKey = String.valueOf(notLikeUserId);
        this.redisTemplate.opsForHash().put(redisKey, hashKey, "1");

        /* 判断用户是否在喜欢列表中，如果存在的话，需要删除数据 */
        if (this.isLike(userId, notLikeUserId)) {
            // 删除Redis中的数据
            redisKey = this.getLikeRedisKey(userId);
            this.redisTemplate.opsForHash().delete(redisKey, hashKey);

            // 删除MongoDB数据
            Query query = Query.query(Criteria.where("userId").is(userId)
                    .and("likeUserId").is(notLikeUserId));
            this.mongoTemplate.remove(query, UserLike.class);
        }

        return true;
    }

    /**
     * 获取 喜欢 RedisKey
     *
     * @param userId 用户id
     * @return
     */
    private String getLikeRedisKey(Long userId) {
        return LIKE_REDIS_KEY_PREFIX + userId;
    }

    /**
     * 获取 喜欢 RedisKey
     *
     * @param userId 用户id
     * @return
     */
    private String getNotLikeRedisKey(Long userId) {
        return NOT_LIKE_REDIS_KEY_PREFIX + userId;
    }


    /**
     * 用户是否喜欢
     *
     * @param userId     用户id
     * @param likeUserId 喜欢用户id
     * @return
     */
    @Override
    public Boolean isLike(Long userId, Long likeUserId) {
        // 先判断Redis中是否有对饮数据记录
        String redisKey = this.getLikeRedisKey(userId);
        String hashKey = String.valueOf(likeUserId);
        Boolean result = this.redisTemplate.opsForHash().hasKey(redisKey, hashKey);

        if (result) {
            return true;
        }
        // 没有再去从MongoDB中查询
        Query query = Query.query(Criteria.where("userId").is(userId)
                .and("likeUserId").is(likeUserId));
        return this.mongoTemplate.count(query, UserLike.class) > 0;
    }

    /**
     * 用户是否不喜欢
     *
     * @param userId     用户id
     * @param likeUserId 喜欢用户id
     * @return
     */
    @Override
    public Boolean isNotLike(Long userId, Long likeUserId) {
        // 查询Redis数据
        String redisKey = this.getNotLikeRedisKey(userId);
        String hashKey = String.valueOf(likeUserId);
        return this.redisTemplate.opsForHash().hasKey(redisKey, hashKey);
    }


    /**
     * 是否相互喜欢
     *
     * @param userId     用户id
     * @param likeUserId 喜欢用户id
     * @return
     */
    @Override
    public Boolean isMutualLike(Long userId, Long likeUserId) {
        return this.isLike(userId, likeUserId)
                && this.isLike(likeUserId, userId);
    }

    /**
     * 查询喜欢用户id列表
     *
     * @param userId 用户id
     * @return
     */
    @Override
    public List<Long> queryLikeIdList(Long userId) {
        String redisKey = this.getLikeRedisKey(userId);
        Set<Object> hashKeys = this.redisTemplate.opsForHash().keys(redisKey);

        /* 判断查询结果，在Redis中没有查询到，就去MongoDB中查询，查询到了就返回并写入Redis，没有则返回空集合 */
        if (CollUtil.isNotEmpty(hashKeys)) {
            // 从Redis中查询到了
            List<Long> likeUserIdList = new ArrayList<>(hashKeys.size());
            hashKeys.forEach(hashKey -> likeUserIdList.add(Convert.toLong(hashKey)));
            return likeUserIdList;

        } else {
            // MongoDB中查询
            Query query = Query.query(Criteria.where("userId").is(userId));
            List<UserLike> userLikeList = this.mongoTemplate.find(query, UserLike.class);

            if (CollUtil.isNotEmpty(userLikeList)) {
                // MongoDB中查询到了数据
                List<Long> likeUserIdList = new ArrayList<>(userLikeList.size());
                userLikeList.forEach(userLike -> likeUserIdList.add(userLike.getLikeUserId()));
                // 存入到Redis中
                likeUserIdList.forEach(likeUserId -> {
                    this.redisTemplate.opsForHash().put(redisKey, String.valueOf(likeUserId), "1");
                });

                return likeUserIdList;
            }

            // MongoDB中也没数据，返回空集合
            return ListUtil.empty();
        }

    }

    /**
     * 查询不喜欢用户id列表
     *
     * @param userId 用户id
     * @return
     */
    @Override
    public List<Long> queryNotLikeList(Long userId) {
        /* 因为不喜欢数据只存在于Redis，所以只用查询Redis集合 */
        String redisKey = this.getNotLikeRedisKey(userId);
        Set<Object> hashKeys = this.redisTemplate.opsForHash().keys(redisKey);

        if (CollUtil.isEmpty(hashKeys)) {
            return ListUtil.empty();
        }

        List<Long> notLikeUserIdList = new ArrayList<>(hashKeys.size());
        hashKeys.forEach(hashKey -> notLikeUserIdList.add(Convert.toLong(hashKey)));

        return notLikeUserIdList;
    }

    /**
     * 相互喜欢的数量
     *
     * @param userId 用户id
     * @return 互相喜欢数
     */
    @Override
    public Long queryMutualLikeCount(Long userId) {
        /* 分析：先查询自己喜欢的用户id，在查询其被我喜欢的用户中，是否也有喜欢我自己的。也就是求交集 */
        // 查询自己喜欢的用户id
        List<Long> likeUserIdList = this.queryLikeIdList(userId);

        // 查询喜欢用户id是否也喜欢我【因为Redis与MongoDB数据不同步，这里就查询Redis】
        String redisKey = getLikeRedisKey(userId);

        Long count = 0L;
        String hashKey = String.valueOf(userId);
        // 查询喜欢用户的喜欢用户，包不包含自己，包含就加一
        for (Long likeUserId : likeUserIdList) {
            redisKey = getLikeRedisKey(likeUserId);
            if (this.redisTemplate.opsForHash().hasKey(redisKey, hashKey)) {
                count++;
            }
        }

        return count;
    }

    /**
     * 喜欢数
     *
     * @param userId 用户id
     * @return 喜欢数【我喜欢】
     */
    @Override
    public Long queryLikeCount(Long userId) {
        String redisKey = this.getLikeRedisKey(userId);
        return this.redisTemplate.opsForHash().size(redisKey);
    }

    /**
     * 粉丝数
     *
     * @param userId 用户id
     * @return 粉丝数【被喜欢】
     */
    @Override
    public Long queryFanCount(Long userId) {
        // 无法通过redis查询完成，必须从Mongodb中查询
        Query query = Query.query(Criteria.where("likeUserId").is(userId));
        return this.mongoTemplate.count(query, UserLike.class);
    }

    /**
     * 查询相互喜欢列表
     *
     * @param userId   用户id
     * @param page     当前页
     * @param pageSize 页大小
     * @return
     */
    @Override
    public PageInfo<UserLike> queryMutualLikeList(Long userId, Integer page, Integer pageSize) {
        // 查询用户喜欢的用户id列表
        List<Long> likeUserIdList = this.queryLikeIdList(userId);
        // 查询用户喜欢的用户也喜欢用户的其用户id

        List<Long> mutualLikeUserIdList = new ArrayList<>();
        String redisKey = null;
        String hashKey = String.valueOf(userId);

        for (Long likeUserId : likeUserIdList) {
            redisKey = this.getLikeRedisKey(likeUserId);
            if (this.redisTemplate.opsForHash().hasKey(redisKey, hashKey)) {
                mutualLikeUserIdList.add(likeUserId);
            }
        }

        Query query = Query.query(Criteria.where("userId").in(mutualLikeUserIdList));
        return this.queryUserLikeByQuery(query, page, pageSize);
    }

    /**
     * 查询我喜欢的列表
     *
     * @param userId   用户id
     * @param page     当前页
     * @param pageSize 页大小
     * @return
     */
    @Override
    public PageInfo<UserLike> queryLikeList(Long userId, Integer page, Integer pageSize) {
        Query query = Query.query(Criteria.where("userId").is(userId));
        return this.queryUserLikeByQuery(query, page, pageSize);
    }

    /**
     * 查询粉丝列表
     *
     * @param userId   用户id
     * @param page     当前页
     * @param pageSize 页大小
     * @return
     */
    @Override
    public PageInfo<UserLike> queryFanList(Long userId, Integer page, Integer pageSize) {
        // 粉丝就是喜欢我的用户们
        Query query = Query.query(Criteria.where("likeUserId").is(userId));
        return this.queryUserLikeByQuery(query, page, pageSize);
    }


    /**
     * Reuse 复用方法 传入查询条件返回分页
     *
     * @param query    查询条件
     * @param page     当前页
     * @param pageSize 页大小
     * @return
     */
    private PageInfo<UserLike> queryUserLikeByQuery(Query query, Integer page, Integer pageSize) {
        PageRequest pageable = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.desc("created")));
        query.with(pageable);

        List<UserLike> userLikeList = this.mongoTemplate.find(query, UserLike.class);
        PageInfo<UserLike> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);
        pageInfo.setRecords(userLikeList);

        return pageInfo;
    }
}
